package com.gulimall.cart.server.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.atguigu.common.utils.R;
import com.atguigu.common.utils.StringUtils;
import com.gulimall.cart.feign.ProductFeignService;
import com.gulimall.cart.server.CartService;
import com.gulimall.cart.thread.CartThreadPoolExecutor;
import com.gulimall.cart.util.ThreadLocalUtil;
import com.gulimall.cart.vo.Cart;
import com.gulimall.cart.vo.CartItem;
import com.gulimall.cart.vo.SkuInfoResp;
import com.gulimall.cart.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author lhj
 * @Date 2024/3/16 14:25
 */
@Slf4j
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    public static final String CART_PREFIX = "gulimall:cart:";

    /**
     * 添加购物车
     *
     * @param skuId   skuId
     * @param cartNum sku数量
     * @return string
     */
    @Override
    public CartItem addToCart(Long skuId, Integer cartNum) {
        try {
            BoundHashOperations operation = getHashOperation();
            String resp = (String) operation.get(skuId.toString());
            if (StringUtils.isNotEmpty(resp)) {
                CartItem cartItem = JSONObject.parseObject(resp, CartItem.class);
                cartItem.setCount(cartItem.getCount() + cartNum);
                operation.put(skuId.toString(), JSONObject.toJSONString(cartItem));
                return cartItem;
            }

            log.info("CartServiceImpl.addToCart com in method skuId: {}, cartNum: {}", skuId, cartNum);
            CartItem cartItem = CartItem.builder()
                    .skuId(skuId)
                    .count(cartNum)
                    .build();

            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                R skuInfoResult = productFeignService.info(skuId);
                if (skuInfoResult.getCode() != 0) {
                    return;
                }
                SkuInfoResp resultData = skuInfoResult.getData("skuInfo", new TypeReference<SkuInfoResp>() {
                });
                cartItem.setPrice(resultData.getPrice());
                cartItem.setImage(resultData.getSkuDefaultImg());
                cartItem.setTitle(resultData.getSkuTitle());
            }, CartThreadPoolExecutor.threadPoolExecutor);

            CompletableFuture<Void> skuAttrFuture = CompletableFuture.runAsync(() -> {
                R skuAttrResult = productFeignService.getSkuAttr(skuId);
                if (skuAttrResult.getCode() != 0) {
                    return;
                }
                List<String> attrResultData = skuAttrResult.getData(new TypeReference<List<String>>() {
                });
                cartItem.setSkuAttrs(attrResultData);
            }, CartThreadPoolExecutor.threadPoolExecutor);

            try {
                CompletableFuture.allOf(skuInfoFuture, skuAttrFuture).get(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } catch (TimeoutException e) {
                throw new RuntimeException(e);
            }
            operation.put(skuId.toString(), JSONObject.toJSONString(cartItem));
            return cartItem;
        } catch (RuntimeException e) {
            log.error("CartServiceImpl.addToCart exception error: {}", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public CartItem getCrtItemBySkuId(Long skuId) {
        BoundHashOperations operation = getHashOperation();
        String resp = (String) operation.get(skuId.toString());
        CartItem cartItem = JSONObject.parseObject(resp, CartItem.class);
        return cartItem;
    }

    /**
     * 获取购物车
     *
     * @return
     */
    @Override
    public Cart getCart() {
        log.info("CartServiceImpl.getCart com in method");
        UserInfoVO userInfoVO = ThreadLocalUtil.get();
        Cart cart = new Cart();
        if (!ObjectUtils.isEmpty(userInfoVO.getUserId())) {
            //获取临时购物车
            List<CartItem> tempCartItem = getTempCartItem();
            if (tempCartItem != null) {
                cart.setCartItems(tempCartItem);
                return cart;
            }
            //获取购物车
            List<CartItem> carts = getCarts();
            if (carts != null) {
                cart.setCartItems(carts);
            }
        } else {
            //获取临时购物车
            String tempCartKey = (CART_PREFIX + userInfoVO.getUserKey());
            BoundHashOperations tempBoundHashOperations = redisTemplate.boundHashOps(tempCartKey);
            List tempCartItems = tempBoundHashOperations.values();
            if (!CollectionUtils.isEmpty(tempCartItems)) {
                List<CartItem> cartItems = new ArrayList<>();
                tempCartItems.stream().forEach(cartTemp -> {
                    String jsonString = (String) cartTemp;
                    CartItem cartItem = JSONObject.parseObject(jsonString, CartItem.class);
                    cartItems.add(cartItem);
                });
                cart.setCartItems(cartItems);
            }
        }
        return cart;
    }

    /**
     * 修改购物项选中状态
     * @param skuId
     * @param check
     * @return
     */
    @Override
    public boolean changeChecked(Long skuId, Integer check) {
        CartItem cartItem = getCrtItemBySkuId(skuId);
        cartItem.setCheck((check == 1 ? true : false));
        putCartItem(skuId, cartItem);
        return true;
    }

    /**
     * 修改购物车数量
     * @param skuId
     * @param cartNum
     * @return
     */
    @Override
    public boolean cartChangeNum(Long skuId, Integer cartNum) {
        CartItem cartItem = getCrtItemBySkuId(skuId);
        cartItem.setCount(cartNum);
        putCartItem(skuId, cartItem);
        return true;
    }

    @Override
    public boolean delCartItem(Long skuId) {
        BoundHashOperations operation = getHashOperation();
        operation.delete(String.valueOf(skuId));
        return true;
    }

    /**
     * 获取用户选中的购物车
     * @return
     */
    @Override
    public List<CartItem> getCheckCartItem() {
        //获取登陆用户的购物车
        UserInfoVO userInfoVO = ThreadLocalUtil.get();
        Long userId = userInfoVO.getUserId();
        String cartKey = CART_PREFIX + userId;
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps(cartKey);
        List values = boundHashOps.values();
        if (values == null || values.size() == 0) {
            return Lists.newArrayList();
        }
        //获取选中的购物车和实时价格
        List<CartItem> cartItems = new ArrayList<>();
        values.stream().forEach(item -> {
            CartItem cartItem = JSONObject.parseObject(item.toString(), CartItem.class);
            if (cartItem.getCheck()) {
                R skuInfoResponse = productFeignService.info(cartItem.getSkuId());
                if (skuInfoResponse.getCode() == 0) {
                    SkuInfoResp skuInfoResp = skuInfoResponse.getData("skuInfo",new TypeReference<SkuInfoResp>() {
                    });
                    cartItem.setPrice(skuInfoResp.getPrice());
                }
                cartItems.add(cartItem);
            }
        });
        return cartItems;
    }

    /**
     * 添加购物项
     * @param skuId
     * @param cartItem
     * @return
     */
    public boolean putCartItem(Long skuId, CartItem cartItem) {
        BoundHashOperations operation = getHashOperation();
        operation.put(String.valueOf(skuId), JSONObject.toJSONString(cartItem));
        return true;
    }

    private List<CartItem> getCarts() {
        UserInfoVO userInfoVO = ThreadLocalUtil.get();
        String cartKey = (CART_PREFIX + userInfoVO.getUserId());
        BoundHashOperations boundHashOps = redisTemplate.boundHashOps(cartKey);
        List cartItems = boundHashOps.values();
        if (!CollectionUtils.isEmpty(cartItems)) {
            List<CartItem> cartItemList = new ArrayList<>();
            cartItems.forEach(cartItemObj -> {
                String cartJson = (String) cartItemObj;
                CartItem cartItem = JSONObject.parseObject(cartJson, CartItem.class);
                cartItemList.add(cartItem);
            });
            return cartItemList;
        }
        return null;
    }

    private List<CartItem> getTempCartItem() {
        UserInfoVO userInfoVO = ThreadLocalUtil.get();
        String tempCartKey = (CART_PREFIX + userInfoVO.getUserKey());
        BoundHashOperations tempBoundHashOperations = redisTemplate.boundHashOps(tempCartKey);
        List tempCartItems = tempBoundHashOperations.values();
        if (!CollectionUtils.isEmpty(tempCartItems)) {
            List<CartItem> cartItems = new ArrayList<>();
            tempCartItems.stream().forEach(cartTemp -> {
                String jsonString = (String) cartTemp;
                CartItem cartItem = JSONObject.parseObject(jsonString, CartItem.class);
                addToCart(cartItem.getSkuId(), cartItem.getCount());
                cartItems.add(cartItem);
            });
            //清除临时购物车
            redisTemplate.delete(tempCartKey);
            return cartItems;
        }
        return null;
    }

    private BoundHashOperations getHashOperation() {
        return redisTemplate.boundHashOps(getRedisKey());
    }


    private String getRedisKey() {
        UserInfoVO userInfoVO = ThreadLocalUtil.get();
        String cartKey = ObjectUtils.isEmpty(userInfoVO.getUserId()) ? userInfoVO.getUserKey() : String.valueOf(userInfoVO.getUserId());
        return CART_PREFIX + cartKey;
    }
}
